package igor.lakienko.ru.generation.quotes.service;

import igor.lakienko.ru.generation.quotes.dto.MessageEventDto;
import igor.lakienko.ru.generation.quotes.dto.StockDto;
import igor.lakienko.ru.generation.quotes.entity.StockEntity;
import igor.lakienko.ru.generation.quotes.enums.Stock;
import igor.lakienko.ru.generation.quotes.mapper.StockMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.ThreadLocalRandom;

/**
 * Сервис для работы с акциями.
 */
@Service
@RequiredArgsConstructor
public class StockService {

    private final StockMapper stockmapper;

    @Value("${quote.responseSleepInMillis}")
    long responseSleepInMillis;

    @Value("${quote.quoteSizeBatch}")
    long quoteSizeBatch;

    /**
     * Постоянное получение акций с задержкой указанной в параметрах.
     */
    public MessageEventDto getStock() {
        try {
            Thread.sleep(responseSleepInMillis);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        StockEntity randomStock = getRandomStock();
        return stockmapper.stockEntityToDto(randomStock);
    }

    /**
     * Получения списка акций в размере указанном в конфигурационном файле.
     */
    public Collection<StockDto> getStocks() {
        ArrayList<StockEntity> stocksEntity = new ArrayList<>();
        for (int i = 0; i < quoteSizeBatch; i++) {
            StockEntity randomStock = getRandomStock();
            stocksEntity.add(randomStock);
        }
        return stockmapper.stocksEntityToDto(stocksEntity);
    }

    /**
     * Получения случайной акции, со случайными параметрами.
     */
    private StockEntity getRandomStock() {
        Stock[] stocks = Stock.values();
        int randomStocks = ThreadLocalRandom.current().nextInt(stocks.length);
        Stock stock = stocks[randomStocks];
        float price = stock.getPrice();
        BigDecimal bidStock = BigDecimal.valueOf(price + ThreadLocalRandom.current()
                .nextDouble(1.0D, 10.0D)).setScale(2, RoundingMode.HALF_DOWN);
        BigDecimal offerStock = BigDecimal.valueOf(price - ThreadLocalRandom.current()
                .nextDouble(1.0D, 10.0D)).setScale(2, RoundingMode.HALF_DOWN);
        BigInteger marketCapital = BigInteger.valueOf(ThreadLocalRandom.current()
                .nextLong(10, 1_000_000_000));
        BigInteger profitCompany = BigInteger.valueOf(ThreadLocalRandom.current().nextLong(10, Long.MAX_VALUE));
        BigInteger stockVolume = BigInteger.valueOf(ThreadLocalRandom.current().nextLong(10, 10_000_000));

        float ebitdaCompany = BigDecimal.valueOf(ThreadLocalRandom.current()
                .nextDouble(1D, 100D)).setScale(2, RoundingMode.HALF_DOWN).floatValue();

        return StockEntity.builder()
                .stock(stock)
                .fullStockName(stock.getDescriptionStock())
                .bid(bidStock)
                .offer(offerStock)
                .price(price)
                .marketCapital(marketCapital)
                .stockVolume(stockVolume)
                .ebitda(ebitdaCompany)
                .dividends(getDividend(price))
                .fairPrice(getFairPrice(price, profitCompany))
                .profitCompany(profitCompany)
                .build();
    }

    private float getDividend(float price) {
        double randomDividends = ThreadLocalRandom.current().nextDouble(1.0D, 15.0D);
        price = (float) ((price * randomDividends) / 100);
        return BigDecimal.valueOf(price).setScale(2, RoundingMode.HALF_DOWN).floatValue();
    }

    private float getFairPrice(float price, BigInteger profitCompany) {
        BigInteger positiveProfit = BigInteger.valueOf(100_000_000);
        float priceRandom = BigDecimal.valueOf(ThreadLocalRandom.current().nextDouble(1.0F, 15.0F))
                .setScale(2, RoundingMode.HALF_DOWN).floatValue();
        if (profitCompany.compareTo(positiveProfit) >= 0) {
            return price + priceRandom;
        } else {
            return price - priceRandom;
        }
    }

}
